<?php abstract class HeatherObject {
	protected $Children = array ();
	protected $LastChild = NULL;

	protected function IsEmptyString ($String) {
		return (preg_match ('[^ \s* $]ux', $String));
	}
	protected function CheckPrefix ($Prefix, $String) {
		if (is_scalar ($String) and is_scalar ($Prefix)) {
			return ($Prefix === substr ($String, 0, strlen ($Prefix)));
		} else return (FALSE);
	}
	protected function ParseWSPrefix ($String) {
		$Matches = array ();
		preg_match ('[^ (\s*) (.*) $]ux', $String, $Matches);
		return (array ($Matches [1], $Matches [2]));
	}


	protected function AddChild (HeatherObject $Object) {
		$this->LastChild = $Object;
		array_push ($this->Children, $this->LastChild);
	}
	protected function UnlinkLastChild () {
		$this->LastChild = NULL;
	}
	protected function NoLastChild () {
		return (NULL === $this->LastChild);
	}
	protected function LastChildClassCheck () {
		if (! $this->NoLastChild ()) foreach (func_get_args () as $Class) if ($this->LastChild instanceof $Class) return (TRUE);
		return (FALSE);
	}

	public function PopBlock () {
		return ($this->NoLastChild ()) ? new HeatherBlock () : call_user_func (array ($this->LastChild, __FUNCTION__));
	}

	protected function __get ($MemberName) {
		if (! property_exists ($this, $MemberName = "_$MemberName")) {
			trigger_error ("Read fault: non-existing member [$MemberName].", E_USER_WARNING);
			return (NULL);
		} else return ($this->$MemberName);;
	}
	protected function __set ($MemberName, $Value) {
		trigger_error ('Direct object member assignment detected. Please, don\'t do that.', E_USER_WARNING);
	}
	protected function __toString () {
		$Result = ('['.get_class ($this)."]");
		foreach (get_object_vars ($this) as $Name => $Value) is_scalar ($Value) and $Result .= "\n  $Name: $Value";
		foreach ($this->Children as $Child) {
			if (is_object ($Child)) {
				foreach (explode ("\n", (string) $Child) as $Line) $Result .= "\n  $Line";
			} else $Result .= "\n  $Child";
		}
		return ($Result);
	}
	protected function XMLTag () {
		return (get_class ($this));
	}
	protected function XMLAttributes () {
		return ('');
	}
	protected function XMLHeader () {
		$Result = '';
		if (property_exists ($this, '_Header') and is_object ($this->_Header)) {
			$Result = call_user_func (array ($this->_Header, 'XMLRender'));
			strlen ($Result) and $Result = "<Header>$Result</Header>";
		}
		return ($Result);
	}
	protected function XMLData () {
		return ('');
	}
	protected function XMLChildren () {
		$Result = '';
		foreach ($this->Children as $Child) $Result .= call_user_func (array ($Child, 'XMLRender'));
		return ($Result);
	}
	public function XMLRender ($OptionsOverride = array ()) {
		$Options = array ('Tags' => TRUE, 'Attributes' => TRUE, 'Header' => TRUE, 'Data' => TRUE, 'Children' => TRUE);
		foreach (array_keys ($Options) as $Option) if (array_key_exists ($Option, $OptionsOverride)) $Options [$Option] = $OptionsOverride [$Option];
		$Tag = ($Options ['Tags']) ? $this->XMLTag () : '';
		$Attributes = ($Options ['Attributes']) ? $this->XMLAttributes () : '';
		$Header = ($Options ['Header']) ? $Header = $this->XMLHeader () : '';
		$Data = $Data = ($Options ['Data']) ? $Data = $this->XMLData () : '';
		$Children = ($Options ['Children']) ? $Children = $this->XMLChildren () : '';
		$Result = $Header.$Data.$Children;
		if (strlen ($Tag) and strlen ($Result)) $Result = "<{$Tag}{$Attributes}>$Result</$Tag>";
		return ($Result);
	}
} ?>